﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Xml;
using System.Xml.Serialization;

namespace SilverCrmSoap.FetchXml
{
    
    [DebuggerStepThrough]
    [XmlRoot("fetch", Namespace = "", IsNullable = false)]
    public class Fetch
    {
        
        /// <summary>
        /// ConvertStringToBytes: Convert a string to a byte array
        /// </summary>
        /// <param name="text">The text to be converted</param>
        /// <returns></returns>
        private static byte[] ConvertStringToBytes(string text)
        {
            var ms = new MemoryStream();
            using (var writer = new StreamWriter(ms) { AutoFlush = true })
            {
                writer.Write(text);
            }
            return ms.ToArray();
        }

        /// <summary>
        /// ConvertBytesToString: Convert Byte Array to a string
        /// </summary>
        /// <param name="bytes">Byte Array</param>
        /// <returns></returns>
        private static string ConvertBytesToString(byte[] bytes)
        {
            string r;
            var ms = new MemoryStream(bytes) { Position = 0 };

            using (var reader = new StreamReader(ms))
            {
                r = reader.ReadToEnd();
            }
            return r;
        }

        /// <summary>
        /// Deserialize: Deserialize Xml to Fetch Object
        /// </summary>
        /// <param name="xml">Xml String</param>
        /// <returns>Fetch Object</returns>
        public static Fetch Deserialize(string xml)
        {
            var byteArray = ConvertStringToBytes(xml);
            var xs = new XmlSerializer(typeof(Fetch));
            var ms = new MemoryStream(byteArray);
            return (Fetch)xs.Deserialize(ms);
        }

        /// <summary>
        /// Serialize: Serialize this Fetch Object into an Xml String
        /// </summary>
        /// <returns>Xml String</returns>
        public string Serialize()
        {
            return Serialize(this);
        }
        
        /// <summary>
        /// Serialize: Serialized the Fetch object into a FetchXml string
        /// </summary>
        /// <param name="f"></param>
        /// <returns></returns>
        public static string Serialize(Fetch f)
        {
            try
            {
                var settings = new XmlWriterSettings { OmitXmlDeclaration = true };

                var ns = new XmlSerializerNamespaces();
                ns.Add(string.Empty, string.Empty);

                var ms = new MemoryStream();
                var xs = new XmlSerializer(typeof(Fetch));
                var xw = XmlWriter.Create(ms, settings);
                
                xs.Serialize(xw, f, ns);
                xw.Flush();

                var r = ConvertBytesToString(ms.ToArray());
                return r.Trim();
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// BuildFetchUsingCrmEntity: Build a Fetch object based on an entity.
        /// </summary>
        /// <param name="entity">The entity object to use as a source</param>
        /// <returns></returns>
        public static Fetch BuildFetchUsingCrmEntity(CrmSdk.Entity entity)
        {
            var fetch = new Fetch();
            fetch.Entity.Name = entity.LogicalName;
            var attributes = new List<Object>();
            attributes.Add(new AllAttributes());
            fetch.Entity.Attributes = attributes;
            fetch.Entity.Filter = new Filter();
            fetch.Entity.Filter.Conditions = new List<Condition>();
            foreach(var attribute in entity.Attributes)
            {
                var condition = new Condition(attribute.Key,Operator.eq,attribute.Value.ToString());
                fetch.Entity.Filter.Conditions.Add(condition);
            }
            return fetch;
        }

        /// <summary>
        /// Fetch Constructor
        /// </summary>
        public Fetch()
        {
            MinActiveRowVersion = false;
            ReturnTotalRecordCount = false;
            NoLock = false;
            Entity = new Entity();
        }

        /// <summary>
        /// Fetch Constructor: Predifned Options
        /// </summary>
        /// <param name="version">The Version</param>
        /// <param name="outputformat">The Format</param>
        /// <param name="mapping">The Mapping</param>
        /// <param name="distinct">Distinct Records Only</param>
        public Fetch(string version, OutputFormat outputformat, Mapping mapping, bool distinct)
        {
            MinActiveRowVersion = false;
            ReturnTotalRecordCount = false;
            NoLock = false;

            Version = version;
            OutputFormat = outputformat;
            Mapping = mapping;
            Distinct = distinct;
            Entity = new Entity();
        }

        [XmlElement("entity", typeof (Entity))]
        public Entity Entity { get; set; }

        [XmlElement("order", typeof (Order))]
        public List<object> Order { get; set; }

        [XmlAttribute("version")]
        public string Version { get; set; }

        [XmlAttribute("count",DataType = "integer")]
        public string Count { get; set; }

        [XmlAttribute("page", DataType = "integer")]
        public string Page { get; set; }

        [XmlAttribute("paging-cookie")]
        public string PagingCookie { get; set; }

        [XmlAttribute("utc-offset")]
        public string UTCOffset { get; set; }

        [XmlAttribute("aggregate")]
        public bool Aggregate { get; set; }

        [XmlIgnore]
        public bool AggregateSpecified { get; set; }

        [XmlAttribute("distinct")]
        public bool Distinct { get; set; }

        [XmlIgnore]
        public bool DistinctSpecified { get; set; }

        [XmlAttribute("mapping")]
        public Mapping Mapping { get; set; }

        [XmlIgnore]
        public bool MappingSpecified { get; set; }

        [XmlAttribute("min-active-row-version"), DefaultValue(false)]
        public bool MinActiveRowVersion { get; set; }

        [XmlAttribute("output-format")]
        public OutputFormat OutputFormat { get; set; }

        [XmlIgnore]
        public bool OutputFormatSpecified { get; set; }

        [XmlAttribute("returntotalrecordcount"), DefaultValue(false)]
        public bool ReturnTotalRecordCount { get; set; }

        [XmlAttribute("no-lock"), DefaultValue(false)]
        public bool NoLock { get; set; }
    }
}